using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;


namespace PaulRz.Util
{
	public delegate bool CleanedEvent(object Message);
    public delegate void DoLog(string log);
	/// <summary>
	/// Implements a Session memory for caching objects which goes through CRMFilter
	/// Session is common for all CRMFilters defined in given windows service (Dispatcher)
	/// </summary>
	public class CacheSet : ICollection, IDisposable
	{
	    public CacheSet(int seconds)
		{
		    InitCleaner(seconds);
		}
		
		~CacheSet()
		{
			Dispose(false);
		}

        private object syncRoot = new object();
        private Dictionary<object, ListItem> values = new Dictionary<object, ListItem>();

		public void AddMs(object obj, int msecToExpire)
		{
			ListItem sci=new ListItem();
			DateTime now=DateTime.Now;
			sci.value=obj;
			sci.Expiration=now+new TimeSpan(0,0,0,0,msecToExpire);
			lock(SyncRoot)
			{
				values.Add(obj,sci);
			}
		}
		
		public void AddMs(object key, object obj, int msecToExpire)
		{
			ListItem sci=new ListItem();
			DateTime now=DateTime.Now;
			sci.value=obj;
			sci.Expiration=now+new TimeSpan(0,0,0,0,msecToExpire);
			lock(SyncRoot)
			{
				values.Add(key,sci);
			}
		}

		public void Add(object obj, int secondsToExpire)
		{
			AddMs(obj, secondsToExpire*1000);
		}

		public void Add(object key,object obj, int secondsToExpire)
		{
			AddMs(key, obj, secondsToExpire*1000);
		}

		public object GetAndRemove(string key)
		{
			ListItem li;
			lock (SyncRoot)
			{
                if (values.ContainsKey(key))
                    li = values[key];
                else
					return null;
				values.Remove(key);
			}
			return li.value;
		}
		
	
		/// <summary>
		/// Returns a hashtable of all attributes from message with specified id
		/// </summary>
		public object this[string key]
		{
			get
			{
				lock(SyncRoot)
				{
				    if (values.ContainsKey(key))
				    {
				        return values[key].value;
				    }
                    else
				    {
				        return null;
				    }
				}
			}
		}

		public bool Contains(string id)
		{
			lock(SyncRoot)
			{
				return values.ContainsKey(id);
			}
		}

		
		public event CleanedEvent Cleaned;
        public event DoLog Log;
	    
	    private void log(string msg)
	    {
            if (Log != null)
                Log(msg);
	    }

	    private bool isCleaning = false;
		/// <summary>
		/// Cleans all expired ContextItems
		/// </summary>
		public void Clean(object dummy)
		{
			if (isCleaning)
                return;

		    isCleaning = true;
			int count=0;
			try
			{

                List<object> removeList = new List<object>();
				
				DateTime currentDate=DateTime.Now;
				lock(SyncRoot)
				{
					foreach(KeyValuePair<object, ListItem> entry in values)
					{
						ListItem sci = entry.Value;
						if(currentDate>sci.Expiration) 
						{
							if(Cleaned!=null)
							{
								if(Cleaned(sci.value)==false)
									continue;
							}
							removeList.Add(entry.Key);
							count++;
						}
					}
					FUtil.removeFrom(values,removeList);
				}

				if(count>0)
					log("Cleaner -  Removed "+count+" of "+(values.Count+count)+" messages.");
			}
			catch (Exception e)
			{
				log("An exception during cleaner "+e);
			}
            finally
			{
			    isCleaning = false;
			}
		}

		
		private Timer cleaner;
		public void InitCleaner(int refreshPeriod) 
		{
			if(cleaner==null)
				cleaner=new Timer(new TimerCallback(Clean), null ,0, refreshPeriod*1000);
		}

	    ///<summary>
	    ///Copies the elements of the <see cref="T:System.Collections.ICollection"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
	    ///</summary>
	    ///
	    ///<param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.ICollection"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing. </param>
	    ///<param name="index">The zero-based index in array at which copying begins. </param>
	    ///<exception cref="T:System.ArgumentNullException">array is null. </exception>
	    ///<exception cref="T:System.ArgumentOutOfRangeException">index is less than zero. </exception>
	    ///<exception cref="T:System.ArgumentException">array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source <see cref="T:System.Collections.ICollection"></see> is greater than the available space from index to the end of the destination array. </exception>
	    ///<exception cref="T:System.InvalidCastException">The type of the source <see cref="T:System.Collections.ICollection"></see> cannot be cast automatically to the type of the destination array. </exception><filterpriority>2</filterpriority>
	    public void CopyTo(Array array, int index)
	    {
	        throw new NotImplementedException();
	    }

	    /// <summary>
		/// Returns count of messages in session
		/// </summary>
		/// <returns></returns>
		public int Count 
		{
			get 
			{
				return values.Count;
			}
		}

		public object SyncRoot
		{
			get { return syncRoot; }
		}

		public bool IsSynchronized
		{
			get { return false; }
		}


		/// <summary>
		/// This function is dangerous because it provides thread unsafe access to internal collection!
		/// </summary>
		/// <returns></returns>
        public IEnumerator GetEnumerator()
		{
			return values.Values.GetEnumerator();
		}

		public void ClearAllMessages()
		{
			lock(SyncRoot)
                values.Clear();
		}

		private void Dispose(bool isDisposing)
		{
			if (cleaner!=null)
			{
				lock(SyncRoot) cleaner.Dispose();
			}
			
			if (isDisposing)
			{
				lock(SyncRoot) values.Clear();
				GC.SuppressFinalize(this);
			}
		}
		
		public void Dispose()
		{
			Dispose(true);
		}
	}

	internal class ListItem
	{
		public DateTime Expiration;
		public object value;
	}

	
		

}

